//
//  NSObject+LKDBHelperEx.m
//  quickBatchManager
//
//  Created by 王定波 on 15/10/10.
//  Copyright © 2015年 jack. All rights reserved.
//

#import "NSObject+LKDBHelperEx.h"



#define LKDBCheck_tableNameIsInvalid(tableName)                           \
if ([LKDBUtils checkStringIsEmpty:tableName]) {                       \
LKErrorLog(@" \n Fail!Fail!Fail!Fail! \n with TableName is nil"); \
return NO;                                                        \
}

#define LKDBCode_Async_Begin    __LKDBWeak LKDBHelper * wself = self; \
[self asyncBlock :^{__strong LKDBHelper *sself = wself;           \
if (sself) {
#define LKDBCode_Async_End      }}];

#define LKDBCheck_modelIsInvalid(model)                                                            \
if (model == nil) {                                                                            \
LKErrorLog(@"model is nil");                                                               \
return NO;                                                                                 \
}                                                                                              \
if ([model.class getModelInfos].count == 0) {                                                  \
LKErrorLog(@"class: %@  property count is 0!!", NSStringFromClass(model.class));           \
return NO;                                                                                 \
}                                                                                              \
NSString *_model_tableName = model.db_tableName ? :[model.class getTableName];                 \
if ([LKDBUtils checkStringIsEmpty:_model_tableName]) {                                         \
LKErrorLog(@"model class name %@ table name is invalid!", NSStringFromClass(model.class)); \
return NO;                                                                                 \
}

@implementation NSObject(LKDBHelperEx)
+(double)sumWithWhere:(NSString*)key where:(id)where, ...
{
    if ([where isKindOfClass:[NSString class]])
    {
        va_list list;
        va_start(list, where);
        where = [[NSString alloc]initWithFormat:where arguments:list];
        va_end(list);
    }
    
    return [[self getUsingLKDBHelper] sum:self key:key where:where];
    
}

+(NSMutableArray*)searchWithWhere:(id)where orderBy:(NSString*)orderBy groupBy:(NSString*)groupBy offset:(NSInteger)offset count:(NSInteger)count
{
    return [[self getUsingLKDBHelper] search:self where:where orderBy:orderBy groupBy:(NSString*)groupBy offset:offset count:count];
}

@end


@implementation LKDBHelper (DatabaseExecuteEx)

- (id)modelValueWithProperty:(LKDBProperty *)property model:(NSObject *)model
{
    id value = nil;
    
    if (property.isUserCalculate) {
        value = [model userGetValueForModel:property];
    } else {
        value = [model modelGetValue:property];
    }
    
    if (value == nil) {
        value = @"";
    }
    
    return value;
}

- (void)asyncBlock:(void (^)(void))block
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block);
}

#pragma mark - row count operation
- (double)sum:(Class)modelClass key:(NSString*)key where:(id)where
{
    return [self sumWithTableName:[modelClass getTableName] key:key where:where];
}

- (void)sum:(Class)modelClass where:(id)where key:(NSString*)key callback:(void (^)(NSInteger))callback
{
    if (callback) {
        LKDBCode_Async_Begin
        NSInteger result = [sself sumWithTableName:[modelClass getTableName] key:key where:where];
        callback(result);
        LKDBCode_Async_End
    }
}

- (double)sumWithTableName:(NSString *)tableName key:(NSString*)key where:(id)where
{
    LKDBCheck_tableNameIsInvalid(tableName);
    
    NSMutableString *rowCountSql = [NSMutableString stringWithFormat:@"select sum(%@) from %@",key, tableName];
    
    NSMutableArray *valuesarray = [self extractQuery:rowCountSql where:where];
    double result = [[self executeScalarWithSQL:rowCountSql arguments:valuesarray] doubleValue];
    
    return result;
}

// splice 'where' 拼接where语句
- (NSMutableArray *)extractQuery:(NSMutableString *)query where:(id)where
{
    NSMutableArray *values = nil;
    
    if ([where isKindOfClass:[NSString class]] && ([LKDBUtils checkStringIsEmpty:where] == NO)) {
        [query appendFormat:@" where %@", where];
    } else if ([where isKindOfClass:[NSDictionary class]]) {
        NSDictionary *dicWhere = where;
        
        if (dicWhere.count > 0) {
            values = [NSMutableArray arrayWithCapacity:dicWhere.count];
            NSString *wherekey = [self dictionaryToSqlWhere:where andValues:values];
            [query appendFormat:@" where %@", wherekey];
        }
    }
    
    return values;
}


// dic where parse
- (NSString *)dictionaryToSqlWhere:(NSDictionary *)dic andValues:(NSMutableArray *)values
{
    NSMutableString *wherekey = [NSMutableString stringWithCapacity:0];
    
    if ((dic != nil) && (dic.count > 0)) {
        NSArray *keys = dic.allKeys;
        
        for (NSInteger i = 0; i < keys.count; i++) {
            NSString *key = [keys objectAtIndex:i];
            id va = [dic objectForKey:key];
            
            if ([va isKindOfClass:[NSArray class]]) {
                NSArray *vlist = va;
                
                if (vlist.count == 0) {
                    continue;
                }
                
                if (wherekey.length > 0) {
                    [wherekey appendString:@" and"];
                }
                
                [wherekey appendFormat:@" %@ in(", key];
                
                for (NSInteger j = 0; j < vlist.count; j++) {
                    [wherekey appendString:@"?"];
                    
                    if (j == vlist.count - 1) {
                        [wherekey appendString:@")"];
                    } else {
                        [wherekey appendString:@","];
                    }
                    
                    [values addObject:[vlist objectAtIndex:j]];
                }
            } else {
                if (wherekey.length > 0) {
                    [wherekey appendFormat:@" and %@=?", key];
                } else {
                    [wherekey appendFormat:@" %@=?", key];
                }
                
                [values addObject:va];
            }
        }
    }
    
    return wherekey;
}

- (NSMutableArray *)search:(Class)modelClass where:(id)where orderBy:(NSString *)orderBy groupBy:(NSString*)groupBy offset:(NSInteger)offset count:(NSInteger)count
{
    return [self searchBase:modelClass columns:nil where:where orderBy:orderBy groupBy:(NSString*)groupBy offset:offset count:count];
}


- (NSMutableArray *)searchBase:(Class)modelClass columns:(id)columns where:(id)where orderBy:(NSString *)orderBy  groupBy:(NSString*)groupBy offset:(NSInteger)offset count:(NSInteger)count
{
    LKDBQueryParams *params = [[LKDBQueryParams alloc]init];
    
    params.toClass = modelClass;
    
    if ([columns isKindOfClass:[NSArray class]]) {
        params.columnArray = columns;
    } else if ([columns isKindOfClass:[NSString class]]) {
        params.columns = columns;
    }
    
    if ([where isKindOfClass:[NSDictionary class]]) {
        params.whereDic = where;
    } else if ([where isKindOfClass:[NSString class]]) {
        params.where = where;
    }
    
    params.orderBy = orderBy;
    params.offset = offset;
    params.count = count;
    params.groupBy = groupBy;
    
    return [self searchBaseWithParams:params];
}


- (NSMutableArray *)searchBaseWithParams:(LKDBQueryParams *)params
{
    if (params.toClass == nil) {
        LKErrorLog(@"you search pars:%@! \n toClass is nil", params.getAllPropertysString);
        return nil;
    }
    
    NSString *db_tableName = params.tableName;
    
    if ([LKDBUtils checkStringIsEmpty:db_tableName]) {
        db_tableName = [params.toClass getTableName];
    }
    
    if ([LKDBUtils checkStringIsEmpty:db_tableName]) {
        LKErrorLog(@"you search pars:%@! \n tableName is empty", params.getAllPropertysString);
        return nil;
    }
    
    NSString *columnsString = nil;
    NSUInteger columnCount = 0;
    
    if (params.columnArray.count > 0) {
        columnCount = params.columnArray.count;
        columnsString = [params.columnArray componentsJoinedByString:@","];
    } else if ([LKDBUtils checkStringIsEmpty:params.columns] == NO) {
        columnsString = params.columns;
        NSArray *array = [params.columns componentsSeparatedByString:@","];
        columnCount = array.count;
    } else {
        columnsString = @"*";
    }
    
    NSMutableString *query = [NSMutableString stringWithFormat:@"select %@,rowid from @t", columnsString];
    NSMutableArray *whereValues = nil;
    
    if (params.whereDic.count > 0) {
        whereValues = [NSMutableArray arrayWithCapacity:params.whereDic.count];
        NSString *wherekey = [self dictionaryToSqlWhere:params.whereDic andValues:whereValues];
        [query appendFormat:@" where %@", wherekey];
    } else if ([LKDBUtils checkStringIsEmpty:params.where] == NO) {
        [query appendFormat:@" where %@", params.where];
    }
    
    [self sqlString:query groupBy:params.groupBy orderBy:params.orderBy offset:params.offset count:params.count];
    
    // replace @t to model table name
    NSString *replaceTableName = [NSString stringWithFormat:@" %@ ", db_tableName];
    
    if ([query hasSuffix:@" @t"]) {
        [query appendString:@" "];
    }
    
    [query replaceOccurrencesOfString:@" @t " withString:replaceTableName options:NSCaseInsensitiveSearch range:NSMakeRange(0, query.length)];
    
    __block NSMutableArray *results = nil;
    [self executeDB:^(FMDatabase *db) {
        FMResultSet *set = nil;
        
        if (whereValues.count == 0) {
            set = [db executeQuery:query];
        } else {
            set = [db executeQuery:query withArgumentsInArray:whereValues];
        }
        
        if (columnCount == 1) {
            results = [self executeOneColumnResult:set];
        } else {
            results = [self executeResult:set Class:params.toClass tableName:db_tableName];
        }
        
        [set close];
    }];
    return results;
}

- (void)sqlString:(NSMutableString *)sql groupBy:(NSString *)groupBy orderBy:(NSString *)orderby offset:(NSInteger)offset count:(NSInteger)count
{
    if ([LKDBUtils checkStringIsEmpty:groupBy] == NO) {
        [sql appendFormat:@" group by %@", groupBy];
    }
    
    if ([LKDBUtils checkStringIsEmpty:orderby] == NO) {
        [sql appendFormat:@" order by %@", orderby];
    }
    
    if (count > 0) {
        [sql appendFormat:@" limit %ld offset %ld", (long)count, (long)offset];
    } else if (offset > 0) {
        [sql appendFormat:@" limit %d offset %ld", INT_MAX, (long)offset];
    }
}

- (NSMutableArray *)executeOneColumnResult:(FMResultSet *)set
{
    NSMutableArray *array = [NSMutableArray arrayWithCapacity:0];
    
    while ([set next]) {
        NSString *string = [set stringForColumnIndex:0];
        
        if (string) {
            [array addObject:string];
        } else {
            NSData *data = [set dataForColumnIndex:0];
            
            if (data) {
                [array addObject:data];
            }
        }
    }
    
    return array;
}


- (NSMutableArray *)executeResult:(FMResultSet *)set Class:(Class)modelClass tableName:(NSString *)tableName
{
    NSMutableArray *array = [NSMutableArray arrayWithCapacity:0];
    LKModelInfos *infos = [modelClass getModelInfos];
    NSInteger columnCount = [set columnCount];
    
    ///当主键是int类型时 会替换掉rowid
    NSString *rowidAliasName = [modelClass db_rowidAliasName];
    
    while ([set next]) {
        NSObject *bindingModel = [[modelClass alloc]init];
        
        for (int i = 0; i < columnCount; i++) {
            NSString *sqlName = [set columnNameForIndex:i];
            LKDBProperty *property = [infos objectWithSqlColumnName:sqlName];
            
            BOOL isRowid = [[sqlName lowercaseString] isEqualToString:@"rowid"];
            
            if ((isRowid == NO) && (property == nil)) {
                continue;
            }
            
            if (isRowid && ((property == nil) || [property.sqlColumnType isEqualToString:LKSQL_Type_Int])) {
                bindingModel.rowid = [set longForColumnIndex:i];
            } else {
                BOOL isUserCalculate = [property.type isEqualToString:LKSQL_Mapping_UserCalculate];
                
                if (property.propertyName && (isUserCalculate == NO)) {
                    NSString *sqlValue = [set stringForColumnIndex:i];
                    [bindingModel modelSetValue:property value:sqlValue];
                    
                    if ([rowidAliasName isEqualToString:sqlName]) {
                        bindingModel.rowid = [set longForColumnIndex:i];
                    }
                } else {
                    NSData *sqlData = [set dataForColumnIndex:i];
                    NSString *sqlValue = [[NSString alloc] initWithData:sqlData encoding:NSUTF8StringEncoding];
                    [bindingModel userSetValueForModel:property value:sqlValue ?:sqlData];
                }
            }
        }
        
        bindingModel.db_tableName = tableName;
        [modelClass dbDidSeleted:bindingModel];
        [array addObject:bindingModel];
    }
    
    return array;
}

@end